﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using Kooboo.HtmlFunc.Providers;

namespace Kooboo.HtmlFunc.Analysis.Testers
{
    public class ParametricTester : StringTester
    {
        const string PartSeparator = ":";
        const string ParaSeparator = ",";
        const string ParenthesesStart = "(";
        const string ParenthesesEnd = ")";
        const string ParenthesesHolder = ParenthesesStart + "val" + ParenthesesEnd;

        public ParametricTester(string referenceText)
            : base(referenceText.Split(PartSeparator.ToCharArray())[0])
        {
            _currentParameterValues = string.Empty;
            var sepIndex = referenceText.IndexOf(PartSeparator[0]);
            if (sepIndex != -1)
            {
                var keyPartString = referenceText.Substring(sepIndex + 1);
                var keyParts = keyPartString.Split(ParaSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                if (keyPartString.IndexOf(ParenthesesHolder) == -1)
                {
                    _parameterNames = keyParts.ToList();
                }
                else
                {
                    _parameterNames = new List<string>();
                    for (var i = 0; i < keyParts.Length; i++)
                        _parameterNames.Add(keyParts[i].Replace(ParenthesesHolder, string.Empty));
                }
            }
        }

        private List<string> _parameterNames;
        private string _currentParameterValues;

        private Dictionary<string, string> _parameters;
        public Dictionary<string, string> Parameters
        {
            get
            {
                if (_parameters == null)
                {
                    _parameters = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
                    var valueParts = _currentParameterValues.Split(ParaSeparator.ToCharArray());
                    for (var i = 0; i < valueParts.Length; i++) { valueParts[i] = valueParts[i].Trim(); }

                    if (_currentParameterValues.IndexOf(ParenthesesStart[0]) == -1)
                    {
                        for (var i = 0; i < _parameterNames.Count; i++)
                        {
                            _parameters.Add(_parameterNames[i], (i < valueParts.Length) ? valueParts[i] : string.Empty);
                        }
                    }
                    else
                    {
                        foreach (var name in _parameterNames)
                        {
                            var part = valueParts.Where(o => o.IgnoreCaseStartsWith(name + ParenthesesStart)).FirstOrDefault();
                            if (string.IsNullOrWhiteSpace(part))
                            {
                                _parameters.Add(name, null);
                            }
                            else
                            {
                                _parameters.Add(name, part.Substring(name.Length + ParenthesesStart.Length).TrimEnd(ParenthesesEnd.ToCharArray()).Trim());
                            }
                        }
                    }
                }
                return _parameters;
            }
        }

        private IList<KeyValuePair<string, string>> _nativeParameters;
        public IList<KeyValuePair<string, string>> NativeParameters
        {
            get
            {
                if (_nativeParameters == null)
                {
                    _nativeParameters = new List<KeyValuePair<string, string>>();
                    var valueParts = _currentParameterValues.Split(ParaSeparator.ToCharArray());
                    for (var i = 0; i < valueParts.Length; i++)
                    {
                        var item = valueParts[i].Trim();
                        if (item.IndexOf(ParenthesesStart[0]) == -1)
                        {
                            _nativeParameters.Add(new KeyValuePair<string, string>(item, null));
                        }
                        else
                        {
                            var parenthesesStartIndex = item.IndexOf(ParenthesesStart[0]);
                            var name = item.Substring(0, parenthesesStartIndex);
                            var value = item.Substring(parenthesesStartIndex + ParenthesesStart.Length).TrimEnd(ParenthesesEnd.ToCharArray()).Trim();
                            _nativeParameters.Add(new KeyValuePair<string, string>(name, value));
                        }
                    }
                }
                return _nativeParameters;
            }
        }

        public string GetParameter(string key, bool exception = true)
        {
            var parameters = this.Parameters;
            if (parameters == null || !parameters.ContainsKey(key))
            {
                if (exception)
                    throw new Exceptions.ParameterMissingExcepition(this.ReferenceText, key);
            }
            return parameters[key];
        }

        public override bool IsMatch(object real)
        {
            var commentNode = real as HtmlCommentNode;

            if (commentNode == null)
            {
                throw new ArgumentException();
            }

            var commentText = CommentTester.GetCommentText(commentNode);

            // the performance of IndexOf(char) is much higher than IndexOf(string).
            var sepIndex = commentText.IndexOf(PartSeparator[0]);

            var testPart = (sepIndex == -1) ? commentText : commentText.Substring(0, sepIndex);

            var match = base.IsMatch(testPart);

            if (match)
            {
                _parameters = null; // set null to trigger rebuild parameters field
                _nativeParameters = null;
                _currentParameterValues = (sepIndex == -1) ? string.Empty : commentText.Substring(sepIndex + 1);
            }

            return match;
        }
    }
}
